<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Nginx流量日志分析工具</title>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }
        .container {
            max-width: 1400px;
            margin: 0 auto;
            background: white;
            border-radius: 15px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }
        header {
            background: linear-gradient(135deg, #2c3e50, #34495e);
            color: white;
            padding: 30px;
            text-align: center;
        }
        h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
        }
        .subtitle {
            font-size: 1.1em;
            opacity: 0.8;
        }
        .upload-section {
            padding: 30px;
            background: #f8f9fa;
            border-bottom: 1px solid #e9ecef;
        }
        .file-input-container {
            position: relative;
            margin-bottom: 20px;
        }
        .file-input {
            width: 100%;
            padding: 15px;
            border: 2px dashed #dee2e6;
            border-radius: 10px;
            background: white;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        .file-input:hover {
            border-color: #667eea;
            background: #f8f9ff;
        }
        .controls {
            display: flex;
            gap: 15px;
            flex-wrap: wrap;
        }
        button {
            padding: 12px 25px;
            border: none;
            border-radius: 8px;
            cursor: pointer;
            font-weight: 600;
            transition: all 0.3s ease;
        }
        .btn-primary {
            background: #667eea;
            color: white;
        }
        .btn-primary:hover {
            background: #5a6fd8;
            transform: translateY(-2px);
        }
        .btn-secondary {
            background: #6c757d;
            color: white;
        }
        .btn-secondary:hover {
            background: #5a6268;
        }
        .btn-success {
            background: #28a745;
            color: white;
        }
        .btn-success:hover {
            background: #218838;
        }
        .dashboard {
            padding: 30px;
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
            gap: 25px;
        }
        .card {
            background: white;
            border-radius: 12px;
            box-shadow: 0 5px 15px rgba(0,0,0,0.08);
            padding: 25px;
            border: 1px solid #e9ecef;
        }
        .card h3 {
            color: #2c3e50;
            margin-bottom: 15px;
            font-size: 1.3em;
            border-bottom: 2px solid #667eea;
            padding-bottom: 8px;
        }
        .stat-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 15px;
        }
        .stat-item {
            text-align: center;
            padding: 15px;
            background: #f8f9fa;
            border-radius: 8px;
        }
        .stat-value {
            font-size: 2em;
            font-weight: bold;
            color: #667eea;
            margin-bottom: 5px;
        }
        .stat-label {
            font-size: 0.9em;
            color: #6c757d;
        }
        .chart-container {
            position: relative;
            height: 300px;
            margin-top: 15px;
        }
        table {
            width: 100%;
            border-collapse: collapse;
            margin-top: 15px;
        }
        th, td {
            padding: 12px 15px;
            text-align: left;
            border-bottom: 1px solid #e9ecef;
        }
        th {
            background: #f8f9fa;
            font-weight: 600;
            color: #2c3e50;
        }
        tr:hover {
            background: #f8f9ff;
        }
        .file-list {
            max-height: 200px;
            overflow-y: auto;
            border: 1px solid #e9ecef;
            border-radius: 8px;
            padding: 15px;
            background: #f8f9fa;
        }
        .file-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 0;
            border-bottom: 1px solid #dee2e6;
        }
        .file-item:last-child {
            border-bottom: none;
        }
        .file-name {
            font-weight: 500;
        }
        .file-size {
            color: #6c757d;
            font-size: 0.9em;
        }
        .loading {
            display: none;
            text-align: center;
            padding: 40px;
        }
        .spinner {
            border: 4px solid #f3f3f3;
            border-top: 4px solid #667eea;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            animation: spin 2s linear infinite;
            margin: 0 auto 20px;
        }
        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        .error {
            color: #dc3545;
            background: #f8d7da;
            padding: 15px;
            border-radius: 8px;
            margin: 15px 0;
            display: none;
        }
        .time-range {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: white;
            padding: 15px;
            border-radius: 8px;
            margin-bottom: 20px;
        }
        .time-range div {
            margin: 5px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>📊 Nginx流量日志分析工具</h1>
            <div class="subtitle">上传从ECS下载的日志文件，自动生成详细分析报告</div>
        </header>

        <div class="upload-section">
            <div class="file-input-container">
                <input type="file" id="fileInput" class="file-input" multiple accept=".log,.txt,.pcap">
                <p style="text-align: center; margin-top: 10px; color: #6c757d;">
                    支持格式: .log, .txt, .pcap 文件 (可多选)
                </p>
            </div>
            
            <div class="controls">
                <button class="btn-primary" onclick="analyzeFiles()">开始分析</button>
                <button class="btn-secondary" onclick="clearAnalysis()">清空分析</button>
                <button class="btn-success" onclick="exportReport()">导出报告</button>
            </div>

            <div class="file-list" id="fileList">
                <div style="text-align: center; color: #6c757d; padding: 20px;">
                    选择的文件将显示在这里
                </div>
            </div>

            <div class="error" id="errorMessage"></div>
        </div>

        <div class="loading" id="loading">
            <div class="spinner"></div>
            <p>正在分析日志文件，请稍候...</p>
        </div>

        <div class="dashboard" id="dashboard" style="display: none;">
            <!-- 时间范围概览 -->
            <div class="card" style="grid-column: 1 / -1;">
                <h3>📅 时间范围概览</h3>
                <div class="time-range" id="timeRange">
                    <!-- 动态生成时间信息 -->
                </div>
            </div>

            <!-- 基础统计 -->
            <div class="card">
                <h3>📈 基础统计</h3>
                <div class="stat-grid" id="basicStats">
                    <!-- 动态生成统计数据 -->
                </div>
            </div>

            <!-- 请求方法分布 -->
            <div class="card">
                <h3>🔧 请求方法分布</h3>
                <div class="chart-container">
                    <canvas id="methodChart"></canvas>
                </div>
            </div>

            <!-- 状态码分布 -->
            <div class="card">
                <h3>🎯 状态码分布</h3>
                <div class="chart-container">
                    <canvas id="statusChart"></canvas>
                </div>
            </div>

            <!-- 流量时间分布 -->
            <div class="card" style="grid-column: span 2;">
                <h3>⏰ 流量时间分布</h3>
                <div class="chart-container">
                    <canvas id="timeChart"></canvas>
                </div>
            </div>

            <!-- 响应时间分析 -->
            <div class="card">
                <h3>⚡ 响应时间分析 (ms)</h3>
                <div class="stat-grid" id="responseTimeStats">
                    <!-- 动态生成响应时间统计 -->
                </div>
            </div>

            <!-- 客户端IP分析 -->
            <div class="card">
                <h3>🌐 客户端IP分析</h3>
                <div id="ipStats">
                    <table>
                        <thead>
                            <tr>
                                <th>IP地址</th>
                                <th>请求数</th>
                                <th>占比</th>
                            </tr>
                        </thead>
                        <tbody id="ipTableBody">
                            <!-- 动态生成IP统计 -->
                        </tbody>
                    </table>
                </div>
            </div>

            <!-- 请求路径分析 -->
            <div class="card" style="grid-column: span 2;">
                <h3>🛣️ 热门请求路径</h3>
                <div style="max-height: 400px; overflow-y: auto;">
                    <table>
                        <thead>
                            <tr>
                                <th>请求路径</th>
                                <th>请求数</th>
                                <th>平均响应时间</th>
                                <th>错误率</th>
                            </tr>
                        </thead>
                        <tbody id="pathTableBody">
                            <!-- 动态生成路径统计 -->
                        </tbody>
                    </table>
                </div>
            </div>

            <!-- 用户代理分析 -->
            <div class="card" style="grid-column: 1 / -1;">
                <h3>🤖 用户代理分析</h3>
                <div style="max-height: 300px; overflow-y: auto;">
                    <table>
                        <thead>
                            <tr>
                                <th>User Agent</th>
                                <th>请求数</th>
                                <th>占比</th>
                            </tr>
                        </thead>
                        <tbody id="userAgentTableBody">
                            <!-- 动态生成User Agent统计 -->
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>

    <script>
        let analysisData = {
            files: [],
            totalRequests: 0,
            timeRange: { start: null, end: null },
            methods: {},
            statusCodes: {},
            responseTimes: [],
            clientIPs: {},
            paths: {},
            userAgents: {},
            hourlyDistribution: {}
        };

        let charts = {};

        // 文件选择处理
        document.getElementById('fileInput').addEventListener('change', function(e) {
            const files = Array.from(e.target.files);
            analysisData.files = files;
            updateFileList(files);
        });

        function updateFileList(files) {
            const fileList = document.getElementById('fileList');
            
            if (files.length === 0) {
                fileList.innerHTML = '<div style="text-align: center; color: #6c757d; padding: 20px;">选择的文件将显示在这里</div>';
                return;
            }

            fileList.innerHTML = files.map(file => `
                <div class="file-item">
                    <span class="file-name">${file.name}</span>
                    <span class="file-size">${formatFileSize(file.size)}</span>
                </div>
            `).join('');
        }

        function formatFileSize(bytes) {
            if (bytes === 0) return '0 Bytes';
            const k = 1024;
            const sizes = ['Bytes', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }

        // 分析文件
        async function analyzeFiles() {
            if (analysisData.files.length === 0) {
                showError('请先选择要分析的日志文件');
                return;
            }

            showLoading(true);
            hideError();

            try {
                // 重置分析数据
                resetAnalysisData();

                for (const file of analysisData.files) {
                    await analyzeFile(file);
                }

                generateDashboard();
                showLoading(false);
                
            } catch (error) {
                showError('分析过程中出现错误: ' + error.message);
                showLoading(false);
            }
        }

        function resetAnalysisData() {
            analysisData = {
                files: analysisData.files,
                totalRequests: 0,
                timeRange: { start: null, end: null },
                methods: {},
                statusCodes: {},
                responseTimes: [],
                clientIPs: {},
                paths: {},
                userAgents: {},
                hourlyDistribution: Array.from({length: 24}, (_, i) => ({ hour: i, count: 0 }))
            };
        }

        async function analyzeFile(file) {
            return new Promise((resolve, reject) => {
                const reader = new FileReader();
                
                reader.onload = function(e) {
                    try {
                        const content = e.target.result;
                        const lines = content.split('\n').filter(line => line.trim());
                        
                        lines.forEach(line => {
                            parseLogLine(line);
                        });
                        
                        resolve();
                    } catch (error) {
                        reject(error);
                    }
                };
                
                reader.onerror = () => reject(new Error('文件读取失败'));
                reader.readAsText(file);
            });
        }

        function parseLogLine(line) {
            // 解析Nginx日志格式
            // 示例格式: 127.0.0.1 - - [01/Jan/2024:03:20:15 +0000] "GET /api/users HTTP/1.1" 200 1234 "http://example.com" "Mozilla/5.0..." 0.045 0.023
            
            const logRegex = /^(\S+) \S+ \S+ \[([^\]]+)\] "(\w+) ([^"]+) ([^"]+)" (\d+) (\d+) "([^"]*)" "([^"]*)" ([0-9.]+) ([0-9.]+)/;
            const match = line.match(logRegex);
            
            if (!match) return;

            const [
                , ip, timestamp, method, path, protocol, statusCode, 
                bytesSent, referer, userAgent, requestTime, upstreamResponseTime
            ] = match;

            // 更新时间范围
            updateTimeRange(timestamp);

            // 统计请求方法
            analysisData.methods[method] = (analysisData.methods[method] || 0) + 1;

            // 统计状态码
            analysisData.statusCodes[statusCode] = (analysisData.statusCodes[statusCode] || 0) + 1;

            // 记录响应时间
            const responseTime = parseFloat(requestTime) * 1000; // 转换为毫秒
            analysisData.responseTimes.push(responseTime);

            // 统计客户端IP
            analysisData.clientIPs[ip] = (analysisData.clientIPs[ip] || 0) + 1;

            // 统计请求路径
            analysisData.paths[path] = analysisData.paths[path] || { count: 0, totalTime: 0, errors: 0 };
            analysisData.paths[path].count++;
            analysisData.paths[path].totalTime += responseTime;
            if (statusCode >= 400) {
                analysisData.paths[path].errors++;
            }

            // 统计用户代理
            if (userAgent && userAgent !== '-') {
                const simplifiedUA = simplifyUserAgent(userAgent);
                analysisData.userAgents[simplifiedUA] = (analysisData.userAgents[simplifiedUA] || 0) + 1;
            }

            // 统计小时分布
            const hour = extractHourFromTimestamp(timestamp);
            if (hour !== null) {
                analysisData.hourlyDistribution[hour].count++;
            }

            analysisData.totalRequests++;
        }

        function updateTimeRange(timestamp) {
            const date = parseNginxTimestamp(timestamp);
            if (!date) return;

            if (!analysisData.timeRange.start || date < analysisData.timeRange.start) {
                analysisData.timeRange.start = date;
            }
            if (!analysisData.timeRange.end || date > analysisData.timeRange.end) {
                analysisData.timeRange.end = date;
            }
        }

        function parseNginxTimestamp(timestamp) {
            // 解析Nginx时间格式: 01/Jan/2024:03:20:15 +0000
            const match = timestamp.match(/^(\d{2})\/(\w{3})\/(\d{4}):(\d{2}):(\d{2}):(\d{2})/);
            if (!match) return null;

            const [, day, month, year, hour, minute, second] = match;
            const monthMap = {
                'Jan': 0, 'Feb': 1, 'Mar': 2, 'Apr': 3, 'May': 4, 'Jun': 5,
                'Jul': 6, 'Aug': 7, 'Sep': 8, 'Oct': 9, 'Nov': 10, 'Dec': 11
            };

            return new Date(
                parseInt(year),
                monthMap[month],
                parseInt(day),
                parseInt(hour),
                parseInt(minute),
                parseInt(second)
            );
        }

        function extractHourFromTimestamp(timestamp) {
            const match = timestamp.match(/:(\d{2}):\d{2}:\d{2}/);
            return match ? parseInt(match[1]) : null;
        }

        function simplifyUserAgent(userAgent) {
            if (userAgent.includes('Chrome')) return 'Chrome';
            if (userAgent.includes('Firefox')) return 'Firefox';
            if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) return 'Safari';
            if (userAgent.includes('Edge')) return 'Edge';
            if (userAgent.includes('MSIE') || userAgent.includes('Trident')) return 'IE';
            if (userAgent.includes('Bot') || userAgent.includes('bot')) return 'Bot/Crawler';
            if (userAgent.includes('Mobile')) return 'Mobile Browser';
            return 'Other';
        }

        function generateDashboard() {
            document.getElementById('dashboard').style.display = 'grid';
            updateTimeRangeDisplay();
            updateBasicStats();
            updateCharts();
            updateTables();
        }

        function updateTimeRangeDisplay() {
            const timeRangeDiv = document.getElementById('timeRange');
            if (analysisData.timeRange.start && analysisData.timeRange.end) {
                const startStr = analysisData.timeRange.start.toLocaleString();
                const endStr = analysisData.timeRange.end.toLocaleString();
                const duration = Math.round((analysisData.timeRange.end - analysisData.timeRange.start) / 1000 / 60); // 分钟
                
                timeRangeDiv.innerHTML = `
                    <div><strong>开始时间:</strong> ${startStr}</div>
                    <div><strong>结束时间:</strong> ${endStr}</div>
                    <div><strong>监控时长:</strong> ${duration} 分钟</div>
                    <div><strong>分析文件数:</strong> ${analysisData.files.length} 个</div>
                `;
            }
        }

        function updateBasicStats() {
            const basicStatsDiv = document.getElementById('basicStats');
            const avgResponseTime = analysisData.responseTimes.length > 0 
                ? analysisData.responseTimes.reduce((a, b) => a + b, 0) / analysisData.responseTimes.length 
                : 0;

            const sortedResponseTimes = [...analysisData.responseTimes].sort((a, b) => a - b);
            const p95Index = Math.floor(sortedResponseTimes.length * 0.95);
            const p95 = sortedResponseTimes.length > 0 ? sortedResponseTimes[p95Index] : 0;

            const errorCount = Object.entries(analysisData.statusCodes)
                .filter(([code]) => code >= 400)
                .reduce((sum, [, count]) => sum + count, 0);

            const errorRate = analysisData.totalRequests > 0 ? (errorCount / analysisData.totalRequests * 100).toFixed(2) : 0;

            basicStatsDiv.innerHTML = `
                <div class="stat-item">
                    <div class="stat-value">${analysisData.totalRequests.toLocaleString()}</div>
                    <div class="stat-label">总请求数</div>
                </div>
                <div class="stat-item">
                    <div class="stat-value">${Object.keys(analysisData.clientIPs).length}</div>
                    <div class="stat-label">独立IP数</div>
                </div>
                <div class="stat-item">
                    <div class="stat-value">${avgResponseTime.toFixed(2)}ms</div>
                    <div class="stat-label">平均响应时间</div>
                </div>
                <div class="stat-item">
                    <div class="stat-value">${errorRate}%</div>
                    <div class="stat-label">错误率</div>
                </div>
            `;

            // 更新响应时间统计
            const responseTimeStatsDiv = document.getElementById('responseTimeStats');
            responseTimeStatsDiv.innerHTML = `
                <div class="stat-item">
                    <div class="stat-value">${Math.min(...analysisData.responseTimes).toFixed(2)}</div>
                    <div class="stat-label">最小(ms)</div>
                </div>
                <div class="stat-item">
                    <div class="stat-value">${avgResponseTime.toFixed(2)}</div>
                    <div class="stat-label">平均(ms)</div>
                </div>
                <div class="stat-item">
                    <div class="stat-value">${p95.toFixed(2)}</div>
                    <div class="stat-label">P95(ms)</div>
                </div>
                <div class="stat-item">
                    <div class="stat-value">${Math.max(...analysisData.responseTimes).toFixed(2)}</div>
                    <div class="stat-label">最大(ms)</div>
                </div>
            `;
        }

        function updateCharts() {
            // 请求方法分布图表
            updateMethodChart();
            
            // 状态码分布图表
            updateStatusChart();
            
            // 时间分布图表
            updateTimeChart();
        }

        function updateMethodChart() {
            const ctx = document.getElementById('methodChart').getContext('2d');
            
            if (charts.methodChart) {
                charts.methodChart.destroy();
            }

            const labels = Object.keys(analysisData.methods);
            const data = Object.values(analysisData.methods);
            const colors = ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF', '#FF9F40'];

            charts.methodChart = new Chart(ctx, {
                type: 'doughnut',
                data: {
                    labels: labels,
                    datasets: [{
                        data: data,
                        backgroundColor: colors,
                        borderWidth: 2,
                        borderColor: '#fff'
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            position: 'bottom'
                        }
                    }
                }
            });
        }

        function updateStatusChart() {
            const ctx = document.getElementById('statusChart').getContext('2d');
            
            if (charts.statusChart) {
                charts.statusChart.destroy();
            }

            const statusGroups = {
                '2xx': 0,
                '3xx': 0,
                '4xx': 0,
                '5xx': 0
            };

            Object.entries(analysisData.statusCodes).forEach(([code, count]) => {
                const firstDigit = code.charAt(0);
                statusGroups[firstDigit + 'xx'] += count;
            });

            const labels = Object.keys(statusGroups);
            const data = Object.values(statusGroups);
            const colors = ['#28a745', '#17a2b8', '#ffc107', '#dc3545'];

            charts.statusChart = new Chart(ctx, {
                type: 'pie',
                data: {
                    labels: labels,
                    datasets: [{
                        data: data,
                        backgroundColor: colors,
                        borderWidth: 2,
                        borderColor: '#fff'
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    plugins: {
                        legend: {
                            position: 'bottom'
                        }
                    }
                }
            });
        }

        function updateTimeChart() {
            const ctx = document.getElementById('timeChart').getContext('2d');
            
            if (charts.timeChart) {
                charts.timeChart.destroy();
            }

            const labels = analysisData.hourlyDistribution.map(item => 
                `${item.hour.toString().padStart(2, '0')}:00`
            );
            const data = analysisData.hourlyDistribution.map(item => item.count);

            charts.timeChart = new Chart(ctx, {
                type: 'line',
                data: {
                    labels: labels,
                    datasets: [{
                        label: '请求数量',
                        data: data,
                        borderColor: '#667eea',
                        backgroundColor: 'rgba(102, 126, 234, 0.1)',
                        borderWidth: 3,
                        fill: true,
                        tension: 0.4
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    scales: {
                        y: {
                            beginAtZero: true,
                            title: {
                                display: true,
                                text: '请求数量'
                            }
                        },
                        x: {
                            title: {
                                display: true,
                                text: '时间 (小时)'
                            }
                        }
                    }
                }
            });
        }

        function updateTables() {
            updateIPTable();
            updatePathTable();
            updateUserAgentTable();
        }

        function updateIPTable() {
            const tbody = document.getElementById('ipTableBody');
            const sortedIPs = Object.entries(analysisData.clientIPs)
                .sort(([,a], [,b]) => b - a)
                .slice(0, 10);

            tbody.innerHTML = sortedIPs.map(([ip, count]) => {
                const percentage = ((count / analysisData.totalRequests) * 100).toFixed(2);
                return `
                    <tr>
                        <td>${ip}</td>
                        <td>${count.toLocaleString()}</td>
                        <td>${percentage}%</td>
                    </tr>
                `;
            }).join('');
        }

        function updatePathTable() {
            const tbody = document.getElementById('pathTableBody');
            const sortedPaths = Object.entries(analysisData.paths)
                .sort(([,a], [,b]) => b.count - a.count)
                .slice(0, 15);

            tbody.innerHTML = sortedPaths.map(([path, data]) => {
                const avgTime = (data.totalTime / data.count).toFixed(2);
                const errorRate = ((data.errors / data.count) * 100).toFixed(2);
                return `
                    <tr>
                        <td title="${path}">${truncateText(path, 50)}</td>
                        <td>${data.count.toLocaleString()}</td>
                        <td>${avgTime}ms</td>
                        <td>${errorRate}%</td>
                    </tr>
                `;
            }).join('');
        }

        function updateUserAgentTable() {
            const tbody = document.getElementById('userAgentTableBody');
            const sortedUAs = Object.entries(analysisData.userAgents)
                .sort(([,a], [,b]) => b - a)
                .slice(0, 10);

            tbody.innerHTML = sortedUAs.map(([ua, count]) => {
                const percentage = ((count / analysisData.totalRequests) * 100).toFixed(2);
                return `
                    <tr>
                        <td title="${ua}">${truncateText(ua, 60)}</td>
                        <td>${count.toLocaleString()}</td>
                        <td>${percentage}%</td>
                    </tr>
                `;
            }).join('');
        }

        function truncateText(text, maxLength) {
            return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
        }

        function showLoading(show) {
            document.getElementById('loading').style.display = show ? 'block' : 'none';
        }

        function showError(message) {
            const errorDiv = document.getElementById('errorMessage');
            errorDiv.textContent = message;
            errorDiv.style.display = 'block';
        }

        function hideError() {
            document.getElementById('errorMessage').style.display = 'none';
        }

        function clearAnalysis() {
            analysisData.files = [];
            document.getElementById('fileInput').value = '';
            document.getElementById('fileList').innerHTML = '<div style="text-align: center; color: #6c757d; padding: 20px;">选择的文件将显示在这里</div>';
            document.getElementById('dashboard').style.display = 'none';
            hideError();
            
            // 销毁所有图表
            Object.values(charts).forEach(chart => chart.destroy());
            charts = {};
        }

        function exportReport() {
            if (analysisData.totalRequests === 0) {
                showError('没有可导出的分析数据');
                return;
            }

            const report = generateTextReport();
            const blob = new Blob([report], { type: 'text/plain' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = `nginx-analysis-report-${new Date().toISOString().split('T')[0]}.txt`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }

        function generateTextReport() {
            let report = 'Nginx流量分析报告\n';
            report += '='.repeat(50) + '\n\n';
            
            report += `生成时间: ${new Date().toLocaleString()}\n`;
            report += `分析文件: ${analysisData.files.length} 个\n`;
            report += `时间范围: ${analysisData.timeRange.start ? analysisData.timeRange.start.toLocaleString() : 'N/A'} - ${analysisData.timeRange.end ? analysisData.timeRange.end.toLocaleString() : 'N/A'}\n\n`;
            
            report += '基础统计:\n';
            report += `  总请求数: ${analysisData.totalRequests.toLocaleString()}\n`;
            report += `  独立IP数: ${Object.keys(analysisData.clientIPs).length}\n`;
            report += `  平均响应时间: ${(analysisData.responseTimes.reduce((a, b) => a + b, 0) / analysisData.responseTimes.length || 0).toFixed(2)}ms\n\n`;
            
            report += '请求方法分布:\n';
            Object.entries(analysisData.methods).forEach(([method, count]) => {
                const percentage = ((count / analysisData.totalRequests) * 100).toFixed(2);
                report += `  ${method}: ${count} (${percentage}%)\n`;
            });
            
            report += '\n状态码分布:\n';
            Object.entries(analysisData.statusCodes).forEach(([code, count]) => {
                const percentage = ((count / analysisData.totalRequests) * 100).toFixed(2);
                report += `  ${code}: ${count} (${percentage}%)\n`;
            });
            
            return report;
        }
    </script>
</body>
</html>